﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using System.Reflection;

namespace CNative.Logging.Settings
{
    public static class SettingsHelpers
    {
        #region SetJsonFileValue
        /// <summary>
        /// 更新.json文件值
        /// </summary>
        /// <param name="fieldPath">属性路径（如：id 、userInfo.id）</param>
        /// <param name="value">属性值</param>
        /// <param name="jsonFilePath"></param>
        public static void SetJsonFileValue(string fieldPath, object value, string jsonFilePath = null)
        {
            if (string.IsNullOrWhiteSpace(fieldPath) || value == null)
                return;
            SetJsonFileValue((jsonObj) => { SetJObjectValue(jsonObj, fieldPath, value); }, jsonFilePath);
        }
        /// <summary>
        /// 批量更新.json文件值
        /// </summary>
        /// <param name="keyValues"></param>
        /// <param name="jsonFilePath"></param>
        public static void SetJsonFileValue(Dictionary<string, object> keyValues, string jsonFilePath = null)
        {
            if (keyValues == null)
                return;
            SetJsonFileValue((jsonObj) => { SetJObjectValue(jsonObj, keyValues); }, jsonFilePath);
        }

        private static void SetJsonFileValue(Action<Newtonsoft.Json.Linq.JObject> action, string jsonFilePath = null)
        {
            if (action == null)
                return;
            if (jsonFilePath == null)
            {
#if NET451
                jsonFilePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
#else
                jsonFilePath = System.IO.Path.Combine(System.AppContext.BaseDirectory, "appsettings.json");
#endif

            }

            var json = System.IO.File.ReadAllText(jsonFilePath);
            var jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);

            action(jsonObj);

            string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
            System.IO.File.WriteAllText(jsonFilePath, output);
        }
        #endregion

        #region SetJsonStrValue
        /// <summary>
        /// 更新json文本值
        /// </summary>
        /// <param name="fieldPath">属性路径（如：id 、userInfo.id）</param>
        /// <param name="value">属性值</param>
        /// <param name="jsonFilePath"></param>
        public static string SetJsonStrValue(this string jsonStr, string fieldPath, object value)
        {
            if (string.IsNullOrWhiteSpace(fieldPath) || value == null)
                return jsonStr;
            return SetJsonStrValue((jsonObj) => { SetJObjectValue(jsonObj, fieldPath, value); }, jsonStr);
        }

        /// <summary>
        /// 批量更新json文本值
        /// </summary>
        /// <param name="keyValues"></param>
        /// <param name="jsonFilePath"></param>
        public static string SetJsonStrValue(this string jsonStr, Dictionary<string, object> keyValues)
        {
            if (keyValues == null)
                return jsonStr;
            return SetJsonStrValue((jsonObj) => { SetJObjectValue(jsonObj, keyValues); }, jsonStr);
        }

        private static string SetJsonStrValue(Action<Newtonsoft.Json.Linq.JObject> action, string jsonStr)
        {
            if (action == null)
                return "";
            if (jsonStr == null)
            {
                return "";
            }

            var json = System.IO.File.ReadAllText(jsonStr);
            var jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);

            action(jsonObj);

            string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
            return output;
        }
        #endregion

        #region SetJObjectValue
        /// <summary>
        /// 批量更新JObject 属性值
        /// </summary>
        /// <param name="jObject">JObject对象</param>
        /// <param name="keyValues">key=fieldPath,value=value</param>
        public static void SetJObjectValue(this JObject jsonObj, Dictionary<string, object> keyValues)
        {
            if (keyValues == null || jsonObj == null)
                return;

            foreach (var dic in keyValues)
            {
                var fieldPath = dic.Key;
                var value = dic.Value;
                if (string.IsNullOrWhiteSpace(fieldPath) || value == null)
                    continue;
                SetJObjectValue(jsonObj, fieldPath, value);
            }
        }
        /// <summary>
        /// 更新JObject 属性值
        /// </summary>
        /// <param name="jObject">JObject对象</param>
        /// <param name="fieldPath">属性路径（如：id 、userInfo.id）</param>
        /// <param name="value">属性值</param>
        public static void SetJObjectValue(this JObject jObject, string fieldPath, object value)
        {
            if (string.IsNullOrWhiteSpace(fieldPath) || value == null || jObject == null)
                return;

            var jsonObj = GetFieldPathObj(jObject, fieldPath, out string filedbame);
            if (jsonObj == null) return;
            dynamic fieldPathValue = jsonObj;
            try
            {
                fieldPathValue[filedbame].Value = value;
            }
            catch (Exception ex)
            {
                try
                {
                    if (fieldPathValue[filedbame].Value is Newtonsoft.Json.Linq.JArray)
                    {
                        var ar = Newtonsoft.Json.Linq.JArray.Parse(value.ToString());
                        fieldPathValue[filedbame].Value = ar;
                    }
                    else
                    {
                        fieldPathValue[filedbame].Value = new JValue(value);
                    }
                }
                catch (Exception ex2)
                {
                    if (fieldPathValue[filedbame] is Newtonsoft.Json.Linq.JArray)
                    {
                        try
                        {
                            var ar = Newtonsoft.Json.Linq.JArray.Parse(value.ToString());
                            fieldPathValue[filedbame] = ar;
                        }
                        catch (Exception ex3) { }
                    }
                    else
                    {

                        try
                        {
                            fieldPathValue[filedbame] = new JValue(value);
                        }
                        catch { }
                    }
                }
            }
        }
        /// <summary>
        /// 更新JObject 属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jObject"></param>
        /// <param name="value"></param>
        /// <param name="fieldPath">属性路径（如：id 、userInfo.id）</param>
        public static void SetJObjectValue<T>(this JObject jObject, T value, string fieldPath = null)
        {
            if (value == null || jObject == null)
                return;

            object jsonObj = jObject;
            if (!string.IsNullOrWhiteSpace(fieldPath) && fieldPath != ".")
                jsonObj = GetFieldPathObj(jsonObj, fieldPath, out string filedbame);
            SetValueRecursively<T>(jsonObj, value);
        }
        #endregion

        #region GetFieldPathObj
        /// <summary>
        /// 取对象属性值（如：id 、userInfo.id）
        /// </summary>
        /// <param name="input"></param>
        /// <param name="fieldPath">属性路径（如：id 、userInfo.id）</param>
        /// <param name="filedName"></param>
        /// <returns></returns>
        private static object GetFieldPathObj(object input, string fieldPath, out string filedName)
        {
            filedName = fieldPath;
            if (string.IsNullOrWhiteSpace(fieldPath) || fieldPath == "." || input == null)
                return input;

            var filedPaths = fieldPath.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
            object val = input;
            object newVal = input;
            for (var i = 0; i < filedPaths.Length; i++)
            {
                filedName = filedPaths[i];
                if (string.IsNullOrWhiteSpace(filedName)) continue;
                if (filedName.Contains("[") && filedName.Contains("]"))//取数组下标值
                {
                    var filedbame = filedName.Substring(0, filedName.IndexOf("["));
                    newVal = GetFieldObj(newVal, filedbame, i < (filedPaths.Length - 1));
                    if (newVal != null)
                    {
                        val = newVal;
                        if (i == filedPaths.Length - 1)
                        {
                            return val;
                        }
                        var index = Convert.ToInt32(GetMatchValue(filedName));
                        if (index >= 0 && newVal is Newtonsoft.Json.Linq.JArray jarray)
                        {
                            if (index >= jarray.Count)
                            {//找不到路径则新增
                                var jobj = new JObject();
                                jarray.Add(jobj);
                                val = newVal = jobj;
                            }
                            else
                            {
                                //var array = jarray.ToObject<List<object>>();
                                newVal = jarray[index];
                            }
                        }
                    }
                    else if (val is Newtonsoft.Json.Linq.JObject _jObject)
                    {//找不到路径则新增
                        var jArray = new Newtonsoft.Json.Linq.JArray();
                        var jobj = new JObject();
                        jArray.Add(jobj);
                        _jObject[filedbame] = jArray;
                        val = newVal = jobj;
                    }
                }
                else//取属性值
                {
                    newVal = GetFieldObj(newVal, filedName, i < (filedPaths.Length - 1));
                    if (newVal == null && val is Newtonsoft.Json.Linq.JObject _jObject)
                    {//找不到路径则新增
                        if (i < filedPaths.Length - 1)
                        {
                            _jObject[filedName] = new JObject();
                            val = newVal = _jObject[filedName];
                        }
                        else { newVal = val; }
                    }
                    else { val = newVal; }
                }

                if (i == filedPaths.Length - 1)
                {
                    val = newVal;
                }
            }
            return val;
        }
        /// <summary>
        /// 取对象属性值
        /// </summary>
        /// <param name="input"></param>
        /// <param name="filedName"></param>
        /// <returns></returns>
        private static object GetFieldObj(object input, string filedName, bool isVal)
        {
            if (input == null)
                return null;
            if (string.IsNullOrWhiteSpace(filedName) || filedName == ".")
                return input;

            filedName = filedName.Trim();
            if (input is Newtonsoft.Json.Linq.JObject _jObject)//JSON
            {
                foreach (var item in _jObject)
                {
                    if (item.Key.Trim().ToLower() == filedName.ToLower())
                    {
                        if (isVal)
                            return _jObject[filedName];
                        else
                            return _jObject;
                    }
                }
            }
            else if (input is Newtonsoft.Json.Linq.JArray _ajObject)
            {
                return input;
            }
            return null;
        }
        private static void SetValueRecursively<T>(dynamic jsonObj, T value)
        {
            if (value == null)
                return;

            var properties = value.GetType().GetProperties();
            foreach (var property in properties)
            {
                var currentValue = property.GetValue(value);
                if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string) || property.PropertyType == typeof(decimal))
                {
                    if (currentValue == null) continue;
                    try
                    {
                        jsonObj[property.Name].Value = currentValue;

                    }
                    catch (Exception)
                    {
                        try
                        {
                            jsonObj[property.Name] = new JValue(currentValue);
                        }
                        catch { }
                    }
                    continue;
                }
                try
                {
                    if (jsonObj[property.Name] == null)
                    {
                        jsonObj[property.Name] = new JObject();
                    }

                }
                catch (Exception)
                {
                    try
                    {
                        jsonObj[property.Name] = new JObject(new JProperty(property.Name));
                    }
                    catch { }

                }
                SetValueRecursively(jsonObj[property.Name], currentValue);
            }
        }

        /// <summary>
        /// 取中括号[]值
        /// </summary>
        /// <param name="template"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static string GetMatchValue(string template, string left = "[", string right = "]")
        {
            var rgx = new System.Text.RegularExpressions.Regex(@"(?i)(?<=\" + left + @")(.*)(?=\" + right + ")");//中括号[]
            var rt = rgx.Match(template).Value;//中括号[]
            return rt;
        }
        #endregion

        #region CommandLineArgs
        /// <summary>
        /// Parse CommandLineArgs
        /// </summary>
        /// <returns></returns>
        public static Dictionary<string, string> ParseArgs()
        {
            var args = Environment.GetCommandLineArgs();
            var extraArgs = new Dictionary<string, string>();
            if (args.Length > 0)
            {
                var argsWithoutLocation = args.ToArray();
                if (argsWithoutLocation.Length > 0)
                {
                    for (var i = 0; i < argsWithoutLocation.Length; i++)
                    {
                        var argument = argsWithoutLocation[i];
                        Console.WriteLine($" argument[{i}]={argument}");
                        if (string.IsNullOrWhiteSpace(argument)) continue;
                        argument = argument.Trim();

                        var key = "";
                        var val = "";
                        if (argument.StartsWith("----"))
                        {
                            key = argument.Substring(4);
                            val = argsWithoutLocation[++i];
                        }
                        else if (argument.StartsWith("---"))
                        {
                            key = argument.Substring(3);
                            val = argsWithoutLocation[++i];
                        }
                        else if (argument.StartsWith("--"))
                        {
                            key = argument.Substring(2);
                            val = argsWithoutLocation[++i];
                        }
                        else if (argument.StartsWith("-"))
                        {
                            key = argument.Substring(1);
                            val = argsWithoutLocation[++i];
                        }
                        else if (argument.Contains("="))
                        {
                            var arguments = argument.Split('=');
                            key = arguments[0];
                            val = arguments[1];
                        }
                        else if (argument.Contains(":"))
                        {
                            var arguments = argument.Split(':');
                            key = arguments[0];
                            val = arguments[1];
                        }
                        if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(val)) continue;

                        key = key.Trim();
                        val = val.Trim();

                        extraArgs[key] = val;
                    }
                }
            }
            return extraArgs;
        }
        #endregion


        #region SetPropertyValue
        /// <summary>
        /// 获取某属性值
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="propertyName">属性名</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public static bool SetPropertyValue(this object obj, string propertyName, object value)
        {
            if (obj == null || obj == DBNull.Value || string.IsNullOrEmpty(propertyName))
                return false;
            var pre = obj.GetType().GetProperty(propertyName,
                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
            if (pre != null && pre.CanWrite)
            {
                //pre.SetValue(obj, value, BindingFlags.GetField | BindingFlags.GetProperty, null, null, CultureInfo.CurrentCulture);
                try
                {
                    //var val = value.ChanageType(pre.PropertyType);
                    //if (val != null) value = val;
                }
                catch { }
                pre.SetValue(obj, value, null);
                return true;
            }
            else
            {
                var filed = obj.GetType().GetField(propertyName,
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
                if (filed != null)
                {
                    filed.SetValue(obj, value);
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 反射执行对象内部方法
        /// </summary>
        /// <param name="target">对象</param>
        /// <param name="methodName">方法名</param>
        /// <param name="paramters">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(this object target, string methodName, params object[] paramters)
        {
            if (target == null)
                throw new Exception("执行对象 target 不能为NULL");
            object _obj = new object();
            try
            {
                var mi = GetMethodInfo(target.GetType(), methodName, paramters);
                _obj = mi.Invoke(target, paramters);
            }
            catch (ArgumentNullException ex)//方法未找到
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(方法未找到)", target), ex);
            }
            catch (MethodAccessException ex)//方法有问题。
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(方法未找到)", target), ex);
            }
            catch (MissingFieldException ex)//找不到该字段或属性。
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(找不到该字段或属性)", target), ex);
            }
            catch (MissingMethodException ex)//方法或参数不匹配
            {
                throw new Exception(OutPutExectStr(methodName, paramters, ex, "插件异常(方法或参数不匹配)", target), ex);
            }
            return _obj;
        }
        private static MethodInfo GetMethodInfo(Type _Type, string _sMethodName, object[] paramters)
        {
            int _iParamCount = paramters == null ? 0 : paramters.Length;
            var _listMethodInfo = _Type.GetMethods().ToList().FindAll(p => p.Name.Equals(_sMethodName));
            var _MethodCount = _listMethodInfo.Count;
            if (_MethodCount == 0)
            {
                if (_iParamCount > 0)
                {
                    Type[] types = Type.GetTypeArray(paramters);

                    return _Type.GetMethod(_sMethodName, BindingFlags.NonPublic | BindingFlags.Instance,//筛选条件
                        Type.DefaultBinder,//绑定
                        types,//参数类型
                        new ParameterModifier[] { new ParameterModifier(_iParamCount) }//参数个数
                    );
                }
                else
                {
                    return _Type.GetMethod(_sMethodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
                }
            }
            else if (_MethodCount == 1)
            {
                return _listMethodInfo[0];
            }
            else if (_MethodCount > 1)
            {
                return _listMethodInfo.ToList().Find(p => p.GetParameters().Count().Equals(_iParamCount));
            }
            return null;
        }
        private static string OutPutArgs(object[] args)
        {
            string _cs = string.Empty;
            foreach (var item in args)
                _cs += item?.ToString() + ",";
            return _cs;
        }
        private static string OutPutExectStr(string methodName, object[] args, Exception _exp, string _Extinfo = "", object target = null)
        {
            string _expStr = string.Empty;
            if (!string.IsNullOrEmpty(_Extinfo))
                _expStr = _Extinfo + "\r\t";
            _expStr += "控件：" + target?.GetType().Module.Name + "\r\t";
            _expStr += "类名：" + target?.GetType().FullName + "\r\t";
            _expStr += "方法：" + methodName + "\r\t";
            _expStr += "参数：" + OutPutArgs(args) + "\r\t";
            _expStr += "堆栈：" + _exp.ToString() + "\r\t";
            //Log(_expStr);
            return _expStr;
        }
        #endregion
    }
}
